package libvirtconn

import (
	"fmt"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"
	"libvirt.org/go/libvirt"
)

// ConnManager 管理 libvirt 连接的生命周期
type ConnManager struct {
	uri          string
	conn         *libvirt.Connect
	mu           sync.RWMutex
	reconnecting bool
	stopChan     chan struct{}
	capabilities *Capabilities
}

// Capabilities 缓存 hypervisor 能力信息
type Capabilities struct {
	Hypervisor     string
	SupportsQEMU   bool
	SupportsKVM    bool
	LibvirtVersion uint32
}

// NewConnManager 创建新的连接管理器
func NewConnManager(uri string) *ConnManager {
	if uri == "" {
		uri = "qemu:///system" // 默认本地系统连接
	}
	return &ConnManager{
		uri:      uri,
		stopChan: make(chan struct{}),
	}
}

// GetURI 获取连接URI
func (c *ConnManager) GetURI() string {
	return c.uri
}

// Connect 建立连接到 libvirt
func (c *ConnManager) Connect() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.conn != nil {
		// 检查现有连接是否有效
		if alive, err := c.conn.IsAlive(); err == nil && alive {
			return nil
		}
		// 清理无效连接
		c.conn.Close()
		c.conn = nil
	}

	log.Infof("Connecting to libvirt: %s", c.uri)
	conn, err := libvirt.NewConnect(c.uri)
	if err != nil {
		return fmt.Errorf("failed to connect to libvirt: %w", err)
	}

	c.conn = conn

	// 获取并缓存能力信息
	if err := c.loadCapabilities(); err != nil {
		log.Warnf("Failed to load capabilities: %v", err)
	}

	log.Info("Successfully connected to libvirt")
	return nil
}

// loadCapabilities 加载并缓存 hypervisor 能力
func (c *ConnManager) loadCapabilities() error {
	caps := &Capabilities{}

	// 获取 libvirt 版本
	version, err := c.conn.GetLibVersion()
	if err == nil {
		caps.LibvirtVersion = version
		log.Infof("Libvirt version: %d.%d.%d",
			version/1000000, (version/1000)%1000, version%1000)
	}

	// 获取 hypervisor 类型
	hvType, err := c.conn.GetType()
	if err == nil {
		caps.Hypervisor = hvType
		caps.SupportsQEMU = hvType == "QEMU"
		caps.SupportsKVM = hvType == "QEMU" // KVM 也通过 QEMU 接口
		log.Infof("Hypervisor type: %s", hvType)
	}

	c.capabilities = caps
	return nil
}

// Close 关闭连接
func (c *ConnManager) Close() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	close(c.stopChan)

	if c.conn != nil {
		if _, err := c.conn.Close(); err != nil {
			return fmt.Errorf("failed to close connection: %w", err)
		}
		c.conn = nil
		log.Info("Libvirt connection closed")
	}
	return nil
}

// WithConn 提供安全的连接访问，带自动重连
func (c *ConnManager) WithConn(fn func(*libvirt.Connect) error) error {
	c.mu.RLock()
	conn := c.conn
	c.mu.RUnlock()

	if conn == nil {
		if err := c.Connect(); err != nil {
			return err
		}
		c.mu.RLock()
		conn = c.conn
		c.mu.RUnlock()
	}

	err := fn(conn)

	// 检查是否为连接错误，需要重连
	if err != nil && isConnectionError(err) {
		log.Warn("Connection error detected, triggering reconnect")
		go c.reconnect()
		return fmt.Errorf("connection lost: %w", err)
	}

	return err
}

// reconnect 后台重连逻辑，带指数退避
func (c *ConnManager) reconnect() {
	c.mu.Lock()
	if c.reconnecting {
		c.mu.Unlock()
		return
	}
	c.reconnecting = true
	c.mu.Unlock()

	defer func() {
		c.mu.Lock()
		c.reconnecting = false
		c.mu.Unlock()
	}()

	backoff := time.Second
	maxBackoff := 30 * time.Second

	for {
		select {
		case <-c.stopChan:
			return
		case <-time.After(backoff):
			if err := c.Connect(); err != nil {
				log.Errorf("Reconnect failed: %v, retrying in %v", err, backoff)
				backoff = minDuration(backoff*2, maxBackoff)
				continue
			}
			log.Info("Reconnection successful")
			return
		}
	}
}

// GetCapabilities 返回缓存的能力信息
func (c *ConnManager) GetCapabilities() *Capabilities {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.capabilities
}

// IsConnected 检查是否已连接
func (c *ConnManager) IsConnected() bool {
	c.mu.RLock()
	defer c.mu.RUnlock()

	if c.conn == nil {
		return false
	}

	alive, err := c.conn.IsAlive()
	return err == nil && alive
}

// isConnectionError 判断是否为连接相关错误
func isConnectionError(err error) bool {
	if err == nil {
		return false
	}

	if lvErr, ok := err.(libvirt.Error); ok {
		switch lvErr.Code {
		case libvirt.ERR_SYSTEM_ERROR,
			libvirt.ERR_RPC,
			libvirt.ERR_NO_CONNECT:
			return true
		}
	}
	return false
}

// minDuration 返回两个 duration 中较小的
func minDuration(a, b time.Duration) time.Duration {
	if a < b {
		return a
	}
	return b
}
